import element.base.Synt;

import element.base.TreeNode;
import element.base.Word;
import element.base.Wordtype;
import element.synt.*;
import symbal.Symbal;
import symbal.SymbalTable;
import symbal.Vtype;
import table.Table;

import java.util.ArrayList;

public class ErrorDeal {
    private Synt compUnit;
    private SymbalTable symbalTable;
    private ArrayList<Symbal> attriTable = new ArrayList<>();
    private Symbal nowFuncD = null; //正被定义的函数块
    private boolean needReturn = false; //是否需要返回值
    private int dewhile = 0; //while层数
    private Vtype nowType = Vtype.VOID; //目前变量的类型
    private ArrayList<Vtype> functypes = new ArrayList<>();//目前函数的调用类型
    private int dupline = 0;
    private boolean isfunc = false;

    public ErrorDeal(Synt compUnit) {
        this.compUnit = compUnit;
        this.symbalTable = Table.getSymbalTable();
    }

    public void execute() {
        dealError(compUnit);
    }

    public void dealError(Synt synt) {
        if (synt instanceof CompUnit) {
            CompUnit_e(synt);
        } else if (synt instanceof Decl) {
            Decl_e(synt);
        } else if (synt instanceof ConstDecl) {
            ConstDecl_e(synt);
        } else if (synt instanceof BType) {
            BType_e(synt);
        } else if (synt instanceof ConstDef) {
            ConstDef_e(synt);
        } else if (synt instanceof ConstInitVal) {
            ConstInitVal_e(synt);
        } else if (synt instanceof VarDecl) {
            VarDecl_e(synt);
        } else if (synt instanceof VarDef) {
            VarDef_e(synt);
        } else if (synt instanceof InitVal) {
            InitVal_e(synt);
        } else if (synt instanceof FuncDef) {
            FuncDef_e(synt);
        } else if (synt instanceof MainFuncDef) {
            MainFuncDef_e(synt);
        } else if (synt instanceof FuncType) {
            FuncType_e(synt);
        } else if (synt instanceof FuncFParams) {
            FuncFParams_e(synt);
        } else if (synt instanceof FuncFParam) {
            FuncFParam_e(synt);
        } else if (synt instanceof Block) {
            Block_e(synt);
        } else if (synt instanceof BlockItem) {
            BlockItem_e(synt);
        } else if (synt instanceof Stmt) {
            Stmt_e(synt);
        } else if (synt instanceof Exp) {
            Exp_e(synt);
        } else if (synt instanceof Cond) {
            Cond_e(synt);
        } else if (synt instanceof LVal) {
            LVal_e(synt);
        } else if (synt instanceof PrimaryExp) {
            PrimaryExp_e(synt);
        } else if (synt instanceof element.synt.Number) {
            Number_e(synt);
        } else if (synt instanceof UnaryExp) {
            UnaryExp_e(synt);
        } else if (synt instanceof UnaryOp) {
            UnaryOp_e(synt);
        } else if (synt instanceof FuncRParams) {
            FuncRParams_e(synt);
        } else if (synt instanceof MulExp) {
            MulExp_e(synt);
        } else if (synt instanceof AddExp) {
            AddExp_e(synt);
        } else if (synt instanceof RelExp) {
            RelExp_e(synt);
        } else if (synt instanceof EqExp) {
            EqExp_e(synt);
        } else if (synt instanceof LAndExp) {
            LAndExp_e(synt);
        } else if (synt instanceof LOrExp) {
            LOrExp(synt);
        } else if (synt instanceof ConstExp) {
            ConstExp(synt);
        }
    }

    public void dealsons(Synt synt) {
        for (TreeNode node: synt.getNodes()) {
            if (node instanceof Synt) {
                dealError((Synt) node);
            }
        }
    }

    public void error(int lindex, char kind) {
        Table.addError(lindex, kind);
    }

    public void goDown() {
        symbalTable = new SymbalTable(symbalTable);
    }

    public void goUp() {
        symbalTable = symbalTable.getFather();
    }

    public void CompUnit_e(Synt synt) {
        dealsons(synt);
    }

    public void Decl_e(Synt synt) {
        dealsons(synt);
    }

    //i
    public void ConstDecl_e(Synt synt) {
        dealsons(synt);
        TreeNode lastnode = synt.getNodes().get(synt.getNodes().size() - 1);
        if (lastnode.isWord(Wordtype.SEMICN)) {
            return;
        }
        error(lastnode.getLindex(), 'i');
    }

    public void BType_e(Synt synt) {
        dealsons(synt);
    }

    //b, k
    public void ConstDef_e(Synt synt) {
        String name = "";
        int de = 0;
        boolean dup = false;
        ArrayList<TreeNode> nodes = synt.getNodes();
        for (TreeNode node: nodes) {
            if (node.isWord(Wordtype.IDENFR)) {
                name = ((Word) node).getContent();
                if (symbalTable.checkDupSymbal(name)) {
                    error(node.getLindex(), 'b');
                    dup = true;
                }
            }
            if (node.isWord(Wordtype.LBRACK)) {
                de++;
                int bindex = synt.getNodes().indexOf(node);
                if (!(nodes.size() > bindex + 2 && nodes.get(bindex + 2).isWord(Wordtype.RBRACK))) {
                    error(nodes.get(bindex + 1).getLindex(), 'k');
                }
            }
        }
        if (!dup) {
            Symbal symbal = new Symbal(name, Vtype.INT);
            if (de == 2) {
                symbal = new Symbal(name, Vtype.INT2);
            } else if (de == 1) {
                symbal = new Symbal(name, Vtype.INT1);
            } else {
                symbal = new Symbal(name, Vtype.INT);
            }
            symbal.setConsts();
            symbalTable.addSymbal(symbal);
        }
        dealsons(synt);
    }

    public void ConstInitVal_e(Synt synt) {
        dealsons(synt);
    }

    //i
    public void VarDecl_e(Synt synt) {
        dealsons(synt);
        TreeNode lastnode = synt.getNodes().get(synt.getNodes().size() - 1);
        if (lastnode.isWord(Wordtype.SEMICN)) {
            return;
        }
        error(lastnode.getLindex(), 'i');
    }

    //b, k
    public void VarDef_e(Synt synt) {
        String name = "";
        int de = 0;
        boolean dup = false;
        ArrayList<TreeNode> nodes = synt.getNodes();
        for (TreeNode node: nodes) {
            if (node.isWord(Wordtype.IDENFR)) {
                name = ((Word) node).getContent();
                if (symbalTable.checkDupSymbal(name)) {
                    error(node.getLindex(), 'b');
                    dup = true;
                }
            }
            if (node.isWord(Wordtype.LBRACK)) {
                de++;
                int bindex = synt.getNodes().indexOf(node);
                if (!(nodes.size() > bindex + 2 && nodes.get(bindex + 2).isWord(Wordtype.RBRACK))) {
                    error(nodes.get(bindex + 1).getLindex(), 'k');
                }
            }
        }
        if (!dup) {
            Symbal symbal = new Symbal(name, Vtype.INT);
            if (de == 2) {
                symbal = new Symbal(name, Vtype.INT2);
            } else if (de == 1) {
                symbal = new Symbal(name, Vtype.INT1);
            } else {
                symbal = new Symbal(name, Vtype.INT);
            }
            symbalTable.addSymbal(symbal);
        }
        dealsons(synt);
    }

    public void InitVal_e(Synt synt) {
        dealsons(synt);
    }

    //b, g, j
    public void FuncDef_e(Synt synt) {
        ArrayList<TreeNode> nodes = synt.getNodes();
        Vtype vtype = Vtype.VOID;
        String name = "";
        for (TreeNode node: nodes) {
            if (node instanceof FuncType) {
                Word type = (Word) ((Synt) node).getNodes().get(0);
                if (type.isWord(Wordtype.VOIDTK)) {
                    vtype = Vtype.VOID;
                    needReturn = false;
                } else {
                    vtype = Vtype.INT;
                    needReturn = true;
                }
            }
            if (node.isWord(Wordtype.IDENFR)) {
                name = ((Word) node).getContent();
                Symbal symbal = new Symbal(name, Vtype.FUNC, vtype);
                if (symbalTable.checkDupSymbal(name)) {
                    error(node.getLindex(), 'b');
                } else {
                    symbalTable.addSymbal(symbal);
                }
                nowFuncD = symbal;
            }
            if (node.isWord(Wordtype.LPARENT)) {
                int bindex = synt.getNodes().indexOf(node);
                if (!(nodes.size() > bindex + 2 && nodes.get(bindex + 2).isWord(Wordtype.RPARENT) || nodes.size() > bindex + 1 && nodes.get(bindex + 1).isWord(Wordtype.RPARENT))) {
                    error(nodes.get(bindex).getLindex(), 'j');
                }
            }
        }
        isfunc = true;
        dealsons(synt);
    }

    //b, g, j
    public void MainFuncDef_e(Synt synt) {
        ArrayList<TreeNode> nodes = synt.getNodes();
        for (TreeNode node: nodes) {
            if (node.isWord(Wordtype.LPARENT)) {
                int bindex = synt.getNodes().indexOf(node);
                if (!(nodes.size() > bindex + 2 && nodes.get(bindex + 2).isWord(Wordtype.RPARENT) || nodes.size() > bindex + 1 && nodes.get(bindex + 1).isWord(Wordtype.RPARENT))) {
                    error(nodes.get(bindex).getLindex(), 'j');
                }
            }
        }
        Symbal symbal = new Symbal("main", Vtype.FUNC, Vtype.INT);
        nowFuncD = symbal;
        needReturn = true;
        isfunc = true;
        dealsons(synt);
    }

    public void FuncType_e(Synt synt) {
        dealsons(synt);
    }

    public void FuncFParams_e(Synt synt) {
        dealsons(synt);
    }

    //b, k
    public void FuncFParam_e(Synt synt) {
        String name = "";
        int de = 0;
        boolean dup = false;
        ArrayList<TreeNode> nodes = synt.getNodes();
        for (TreeNode node: nodes) {
            if (node.isWord(Wordtype.IDENFR)) {
                name = ((Word) node).getContent();
//                if (symbalTable.checkDupSymbal(name)) {
//                    error(node.getLindex(), 'b');
//                    dup = true;
//                }
                for (Symbal symbal1: attriTable) {
                    if (name.equals(symbal1.getName())) {
                        dupline = node.getLindex();
                    }
                }
            }
            if (node.isWord(Wordtype.LBRACK)) {
                de++;
                int bindex = synt.getNodes().indexOf(node);
                int gap = 2;
                if (de == 1) {
                    gap = 1;
                }
                if (!(nodes.size() > bindex + gap && nodes.get(bindex + gap).isWord(Wordtype.RBRACK))) {
                    error(nodes.get(bindex + gap - 1).getLindex(), 'k');
                }
            }
        }
        if (!dup) {
            Symbal symbal = new Symbal(name, Vtype.INT);
            if (de == 2) {
                symbal = new Symbal(name, Vtype.INT2);
            } else if (de == 1) {
                symbal = new Symbal(name, Vtype.INT1);
            } else {
                symbal = new Symbal(name, Vtype.INT);
            }
            attriTable.add(symbal);
        }
        dealsons(synt);
    }

    public void Block_e(Synt synt) {
        ArrayList<TreeNode> nodes = synt.getNodes();
        int len = nodes.size();
        if (len > 2) {
            Synt lastItem = (Synt) nodes.get(len - 2);
            if (needReturn && isfunc) {
                if (!lastItem.findFirst().isWord(Wordtype.RETURNTK)) {
                    error(nodes.get(len - 1).getLindex(), 'g');
                }
            }
        } else {
            if (needReturn && isfunc) {
                error(nodes.get(len - 1).getLindex(), 'g');
            }
        }
        if (!needReturn) {
            for (TreeNode node: nodes) {
                if (node instanceof Synt) {
                    Word first = ((Synt) node).findFirst();
                    if (first.isWord(Wordtype.RETURNTK)) {
                        if (((Synt) ((Synt) node).getNodes().get(0)).getNodes().size() > 2) {
                            error(first.getLindex(), 'f');
                        }
                    }
                }
            }
        }
//        needReturn = false;
        isfunc = false;
        goDown();
        if (nowFuncD != null) {
            for (Symbal symbal: attriTable) {
                if (symbalTable.checkDupSymbal(symbal.getName())) {
                    error(dupline, 'b');
                } else {
                    symbalTable.addSymbal(symbal);
                }
                nowFuncD.addAttr(symbal.getVtype());
            }
        }
        attriTable = new ArrayList<>();
        nowFuncD = null;
        dealsons(synt);
        goUp();
    }

    public void BlockItem_e(Synt synt) {
        dealsons(synt);
    }

    //a, f, h, i, j, l, m
    public void Stmt_e(Synt synt) {
        ArrayList<TreeNode> nodes = synt.getNodes();
        if (synt.findFirst().isWord(Wordtype.IFTK)) {
            if (!nodes.get(3).isWord(Wordtype.RPARENT)) {
                error(nodes.get(2).getLindex(), 'j');
            }
        } else if (synt.findFirst().isWord(Wordtype.WHILETK)) {
            if (!nodes.get(3).isWord(Wordtype.RPARENT)) {
                error(nodes.get(2).getLindex(), 'j');
            }
        } else if (synt.findFirst().isWord(Wordtype.BREAKTK)) {
            if (dewhile == 0) {
                error(synt.findFirst().getLindex(), 'm');
            }
        } else if (synt.findFirst().isWord(Wordtype.CONTINUETK)) {
            if (dewhile == 0) {
                error(synt.findFirst().getLindex(), 'm');
            }
        } else if (synt.findFirst().isWord(Wordtype.RETURNTK)) {
            if (!nodes.get(nodes.size() - 1).isWord(Wordtype.SEMICN)) {
                error(nodes.get(nodes.size() - 1).getLindex(), 'i');
            }
        } else if (synt.findFirst().isWord(Wordtype.PRINTFTK)) {
            if (nodes.get(2).isWord(Wordtype.STRCON)) {
                String fstr = ((Word) nodes.get(2)).getContent();
                if (fstr.equals("error")) {
                    error(nodes.get(2).getLindex(), 'a');
                } else {
                    int fn = 0;
                    for (int i = 0; i < fstr.length(); i++) {
                        if (fstr.charAt(i) == '%') {
                            fn++;
                        }
                    }
                    int rn = 0;
                    for (int i = 0; i < nodes.size(); i++) {
                        if (nodes.get(i).isWord(Wordtype.COMMA)) {
                            rn++;
                        }
                    }
                    if (fn != rn) {
                        error(nodes.get(0).getLindex(), 'l');
                    }
                }
            }
            if (!nodes.get(nodes.size() - 1).isWord(Wordtype.SEMICN)) {
                error(nodes.get(nodes.size() - 1).getLindex(), 'i');
            } else if (!nodes.get(nodes.size() - 2).isWord(Wordtype.RPARENT)) {
                error(nodes.get(nodes.size() - 2).getLindex(), 'j');
            }
        } else if (nodes.get(0) instanceof LVal) {
            if (nodes.get(1).isWord(Wordtype.ASSIGN)) {
                Word firstcon = synt.findFirst();
                String name = firstcon.getContent();
                if (symbalTable.checkSymbals(name)) {
                    Symbal symbal = symbalTable.findSymbal(name);
                    if (symbal.isConsts()) {
                        error(firstcon.getLindex(), 'h');
                    }
                }
                if (nodes.get(2).isWord(Wordtype.GETINTTK)) {
                    if (!nodes.get(4).isWord(Wordtype.RPARENT)) {
                        error(nodes.get(3).getLindex(), 'j');
                    }
                }
            }
            if (!nodes.get(nodes.size() - 1).isWord(Wordtype.SEMICN)) {
                error(nodes.get(nodes.size() - 1).getLindex(), 'i');
            }
        } else if (!(nodes.get(0) instanceof Block)) {
            if (!nodes.get(nodes.size() - 1).isWord(Wordtype.SEMICN)) {
                error(nodes.get(nodes.size() - 1).getLindex(), 'i');
            }
        }
        if (synt.findFirst().isWord(Wordtype.WHILETK)) {
            dewhile++;
        }
        dealsons(synt);
        if (synt.findFirst().isWord(Wordtype.WHILETK)) {
            dewhile--;
        }
    }

    public void Exp_e(Synt synt) {
        dealsons(synt);
    }

    public void Cond_e(Synt synt) {
        dealsons(synt);
    }

    //c, k
    public void LVal_e(Synt synt) {
        dealsons(synt);
        Symbal symbal = new Symbal("ini", Vtype.VOID);
        int de = 0;
        Word firstcon = synt.findFirst();
        String name = firstcon.getContent();
        if (!symbalTable.checkSymbals(name)) {
            System.out.println(Table.getSymbalTable().toString());
            error(firstcon.getLindex(), 'c');
        } else {
            symbal = symbalTable.findSymbal(name);
            if (symbal.getVtype().equals(Vtype.FUNC)) {
                error(firstcon.getLindex(), 'c');
            } else {
                if (symbal.getVtype().equals(Vtype.INT)) {
                    de = 0;
                } else if (symbal.getVtype().equals(Vtype.INT1)) {
                    de = 1;
                } else if (symbal.getVtype().equals(Vtype.INT2)) {
                    de = 2;
                }
            }
        }
        ArrayList<TreeNode> nodes = synt.getNodes();
        for (TreeNode node: nodes) {
            if (node.isWord(Wordtype.LBRACK)) {
                int bindex = synt.getNodes().indexOf(node);
                if (!(nodes.size() > bindex + 2 && nodes.get(bindex + 2).isWord(Wordtype.RBRACK))) {
                    error(nodes.get(bindex + 1).getLindex(), 'k');
                }
                de--;
            }
        }
        if (de == 0) {
            nowType = Vtype.INT;
        } else if (de == 1) {
            nowType = Vtype.INT1;
        } else if (de == 2) {
            nowType = Vtype.INT2;
        } else {
            nowType = Vtype.VOID;
        }
    }

    public void PrimaryExp_e(Synt synt) {
        dealsons(synt);
        if (synt.getNodes().get(0).isWord(Wordtype.LPARENT)) {
            if (!synt.getNodes().get(synt.getNodes().size() - 1).isWord(Wordtype.RPARENT)) {
                error(synt.getNodes().get(synt.getNodes().size() - 2).getLindex(), 'j');
            }
        }
    }

    public void Number_e(Synt synt) {
        dealsons(synt);
        nowType = Vtype.INT;
    }


    //c, d, e, j
    public void UnaryExp_e(Synt synt) {
        dealsons(synt);
        ArrayList<TreeNode> nodes = synt.getNodes();
//        for (int i = nodes.size() - 1; i >= 0; i--) {
//            if (nodes.get(i) instanceof Synt) {
//                dealError((Synt) nodes.get(i));
//            }
//        }
        if (nodes.get(0).isWord(Wordtype.IDENFR) && nodes.get(1).isWord(Wordtype.LPARENT)) {
            if (!nodes.get(nodes.size() - 1).isWord(Wordtype.RPARENT)) {
                error(nodes.get(nodes.size() - 1).getLindex(), 'j');
            }
            Word firstcon = synt.findFirst();
            String name = firstcon.getContent();
            if (!symbalTable.checkSymbals(name)) {
                System.out.println(Table.getSymbalTable().toString());
                error(firstcon.getLindex(), 'c');
                nowType = Vtype.VOID;
            } else {
                Symbal nowfunc = symbalTable.findSymbal(name);
                if (!nowfunc.getVtype().equals(Vtype.FUNC)) {
                    error(firstcon.getLindex(), 'c');
                } else {
                    int l1 = functypes.size();
                    int l2 = nowfunc.getAttrtypes().size();
                    if (l1 != l2) {
                        error(firstcon.getLindex(), 'd');
                        System.out.println(nowfunc.getAttrtypes());
                        System.out.println(functypes);
                    } else {
                        if (!nowfunc.getAttrtypes().equals(functypes)) {
                            error(firstcon.getLindex(), 'e');
                            System.out.println(nowfunc.getAttrtypes());
                            System.out.println(functypes);
                        }
                    }
                    functypes = new ArrayList<>();
                    nowType = nowfunc.getReturntype();
                }
            }
        }
    }

    public void UnaryOp_e(Synt synt) {
        dealsons(synt);
    }

    public void FuncRParams_e(Synt synt) {
        ArrayList<Vtype> temp = new ArrayList<>();
        for (TreeNode node: synt.getNodes()) {
            if (node instanceof Synt) {
                dealError((Synt) node);
                temp.add(nowType);
            }
        }
        functypes = temp;
    }

    public void MulExp_e(Synt synt) {
        dealsons(synt);
    }

    public void AddExp_e(Synt synt) {
        dealsons(synt);
    }

    public void RelExp_e(Synt synt) {
        dealsons(synt);
    }

    public void EqExp_e(Synt synt) {
        dealsons(synt);
    }

    public void LAndExp_e(Synt synt) {
        dealsons(synt);
    }

    public void LOrExp(Synt synt) {
        dealsons(synt);
    }

    public void ConstExp(Synt synt) {
        dealsons(synt);
    }
}
